package biolabs.mcl

import biolabs.core.Application
import biolabs.mcl.syntax.DelegateDesignationAST

import java.lang.reflect.Method

object DelegateDesignation {
	def analyze(ast: DelegateDesignationAST, defaultAction: ActualType, model: ModelDefinition, app: Application): AnalysisResult[DelegateDesignation] = {
		// find a supertype corresponding to each source type
		val sTypes = model.superTypes filter { _.superType.name == ast.typeName }
		if(sTypes.isEmpty)
			return AnalysisError(ast.typeName + " is not a declared supertype")
		val superType = sTypes.head.superType

		// analyze interaction
		val iaType = ast.usedAction match {
			case Some(action) =>
				SingleType.analyze(action, model, model.typeParameterMap, app) match {
					case AnalysisSuccess(t) => t match {
						case u: ActualType =>
							if(defaultAction.isAssignableFrom(u))
								u
							else
								return AnalysisError(u + " is not a subtype of " + defaultAction)
						case v:
							TypeVar => return AnalysisError("Cannot use type variable " + v.name + " as an action type.")
					}
					case AnalysisError(msg) => return AnalysisError(msg)
				}
			case None =>
				defaultAction
		}

		// check that the supertype has exactly one delegate candidate
		// (i.e. delegate-providing method) for the given interaction
		val delegateMethod = superType.getDelegateCandidates(iaType) match {
			case Nil =>
				return AnalysisError(ScalaCodeGen.refer(superType) + " has no delegate for " + ScalaCodeGen.refer(iaType))
			case dm::Nil =>
				dm.method
			case _ =>
				return AnalysisError("In type " + ScalaCodeGen.refer(superType) +
					": Ambiguity of delegate method for " + ScalaCodeGen.refer(iaType) +
					". Candidates are: " + superType.delegateCandidates.map{_.method}.mkString(", "))
		}

		AnalysisSuccess(new DelegateDesignation(superType, iaType, delegateMethod))
	}
}

class DelegateDesignation private (val superType: ActualType, val sourceInteraction: ActualType, val delegateMethod: Method)